﻿Imports System.Runtime.InteropServices
Module Main
  Private _mainThreadPool As New List(Of Threading.Thread)
  Private _mn As AvianPlayMain = Nothing

  Public ReadOnly Property MainThreadPool() As List(Of Threading.Thread)
    Get
      Return _mainThreadPool
    End Get
  End Property

  Public ReadOnly Property MainFormReference() As AvianPlayMain
    Get
      Return _mn
    End Get
  End Property

  Public Sub Main()
    Try
      FixReadOnlyAndCleanup() ' Make sure the key files are not set to read-only

      Dim ae As AudioEngine = Nothing ' Has to be initialized here, so we can properly clean up the audio engine process even if an exception is thrown somewhere higher up
      Dim pcm As PodcastManager = Nothing ' Has to be initalized here because the Long Interval Timer can dangle a thread if an exception is thrown higher up; this allows us to clean it up here
      Dim mn As AvianPlayMain = Nothing

      Try
        ErrorLog.Language = Language.GetLanguageReader
        ErrorLog.LogPath = Settings.ErrorLogPath
        ErrorLog.MaxRows = Settings.ErrorLogLengthRows
        ErrorLog.WriteToErrorLog("<<< Application Start >>> " & System.Reflection.Assembly.GetExecutingAssembly.GetName.Version.ToString)
        ae = New AudioEngine
        pcm = New PodcastManager
        _mn = New AvianPlayMain
      Catch ex As TerminateWithoutReportException
        Throw
      Catch ex As Exception
        MessageBox.Show("Unable to initialize Avian Play: " & ex.Message, "Avian Play", MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1)
        Throw
      End Try

      Try
        'ExtendedGraphicsMethods.InitGDIPlus()
        ae.StartAudioEngine()
        _mn.SetAudioEngine(ae)
        _mn.SetPodcastManager(pcm)
        Application.Run(_mn)
      Catch ex As Exception When _mn.GracefulExit = True
        ' Ignore any exceptions during a graceful exit (sql can sometimes throw when in the middle of an operation)

        ' Terminate all threads!
        TerminateAllDanglingThreads()
      Catch ex As TerminateWithoutReportException
        Throw
      Catch ex As Exception
        ' Terminate all threads!
        TerminateAllDanglingThreads()

        ' Write out the error
        ErrorLog.WriteToErrorLog("!! APPLICATION CRASH !!" & vbCrLf & ex.ToString)

        Dim errString As String = "An error has occurred which has caused Avian Play to crash.  For information on the error, see the error log file.  For help with this error, post this error to the forums for the Avian Play online community.  Exception:"
        If Language.IsLanguageKeyValid("AppCrashError") Then
          errString = Language.GetLanguageString("AppCrashError")
        End If

        MessageBox.Show(errString & " " & ex.GetType.ToString & " / " & ex.Message, "Avian Play", MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1)
      Finally
        ' Make sure we terminate the audio engine! (don't want to leave APEngine.exe running)
        If ae IsNot Nothing Then ae.EndAudioEngine()

        ' Make sure the podcast manager is stopped! (the long interval timer used to wake the device periodically to check the subscriptions can cause the process to hang if not stopped before process exit)
        If pcm IsNot Nothing Then
          pcm.RaiseEvents = False
          pcm.Stop()
        End If

        ' Deinit GDI+
        'ExtendedGraphicsMethods.InitGDIPlus()
      End Try
    Catch ex As TerminateWithoutReportException
      ' Ignore here because this exception is only thrown when reporting is already done and we just want to terminate
    Catch ex As Exception
      ErrorLog.WriteToErrorLog("Crash while terminating application: " & vbCrLf & ex.ToString)
    Finally
      Try : SpinDanglingThreads() : Catch : End Try
      Try : ErrorLog.WriteToErrorLog("<<< Application End >>>") : Catch : End Try
      Try : Application.Exit() : Catch : End Try
    End Try
  End Sub

  Friend Sub TerminateAllDanglingThreads()
    ' Terminate all threads
    SyncLock MainThreadPool
      For Each t As Threading.Thread In MainThreadPool
        Try
          t.Abort()
        Catch
          ' Eat all the exceptions when forcefully terminating dangling threads
        End Try
      Next
    End SyncLock
  End Sub

  Friend Sub SpinDanglingThreads()
    For Each t As Threading.Thread In MainThreadPool
      Try
        t.Join()
      Catch
        ' Eat all the exceptions when forcefully terminating dangling threads
      End Try
    Next
  End Sub

  Friend Sub FixReadOnlyAndCleanup()
    Dim root As String = Settings.ApplicationPath

    Try
      For Each file As String In IO.Directory.GetFiles(root)
        Try
          Dim attr As New IO.FileInfo(file)
          If attr.Attributes And IO.FileAttributes.ReadOnly = IO.FileAttributes.ReadOnly Then
            attr.Attributes = attr.Attributes And Not IO.FileAttributes.ReadOnly
          End If
        Catch
        End Try
      Next
    Catch
    End Try

    Try
      For Each file As String In IO.Directory.GetFiles(root & "\INI")
        Try
          Dim attr As New IO.FileInfo(file)
          If attr.Attributes And IO.FileAttributes.ReadOnly = IO.FileAttributes.ReadOnly Then
            attr.Attributes = attr.Attributes And Not IO.FileAttributes.ReadOnly
          End If
        Catch
        End Try
      Next
    Catch
    End Try

    Try
      If IO.File.Exists(root & "\multicab.exe") Then
        IO.File.Delete(root & "\multicab.exe")
      End If
    Catch
    End Try
  End Sub

  Public Class TerminateWithoutReportException
    Inherits Exception
  End Class
End Module
